﻿using System;
using System.IO;
using System.Net;
using System.Text;

using Builderator.Core.Plugin;
using Builderator.Core.Interfaces;

namespace Builderator.Core.Action
{
    public class GoogleCodePlugin : PluginBase, IDeploy
    {
        [DocumentProperty("Project", typeof(String), false)]
        public string Project
        {
            get;
            set;
        }

        [DocumentProperty("Username", typeof(String), false)]
        public string Username
        {
            get;
            set;
        }

        [DocumentProperty("Password", typeof(String), false)]
        public string Password
        {
            get;
            set;
        }

        [DocumentProperty("Summary", typeof(String), false)]
        public string Summary
        {
            get;
            set;
        }

        [DocumentProperty("Description", typeof(String), false)]
        public String Description
        {
            get;
            set;
        }

        [DocumentProperty("Artifacts", typeof(Uri), false, DocumentProperty.BrowseUriType.File, "All Files|*.*")]
        public string File
        {
            get;
            set;
        }

        [DocumentProperty("Labels", typeof(List), false)]
        public List Labels
        {
            get;
            set;
        }

        public GoogleCodePlugin()
            : base("GoogleCode", new Guid("A7A5982A-FF8B-11E0-9E71-04D54824019B"))
        {

        }

        public override ExecutionResult Run()
        {
            if (!Directory.Exists(this.File))
            {
                this.UploadFile(this.File);
            }
            else
            {
                foreach (string file in Directory.GetFiles(this.File))
                {
                    this.UploadFile(file);
                }
            }

            return ExecutionResult.Success;
        }

        public void UploadFile(string fileName)
        {
            Uri artifactUri = new Uri(this.File);
            Uri uploadUri = new Uri(string.Format("https://{0}.googlecode.com/files", this.Project));

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uploadUri);

            request.Method = "POST";

            request.ContentType = "multipart/form-data; boundary=BASSHAMMAHSSAB";

            request.UserAgent = "Builderator Uploader";

            request.Headers.Add(HttpRequestHeader.Authorization, string.Format("Basic {0}", this.GetAuthToken()));

            request.KeepAlive = false;

            request.Timeout = 20 * 60 * 1000;

            using (Stream requestStream = request.GetRequestStream())
            {
                StringBuilder query = new StringBuilder();

                query.AppendLine("--BASSHAMMAHSSAB");

                query.AppendLine("Content-Disposition: form-data; name=\"summary\"");

                query.AppendLine();

                if (this.Summary == null || this.Summary == string.Empty)
                {
                    query.AppendLine("Uploaded from Builderator");
                }
                else
                {
                    query.AppendLine(this.Summary);
                }

                query.AppendLine("--BASSHAMMAHSSAB");

                query.AppendLine("Content-Disposition: form-data; name=\"description\"");

                query.AppendLine();

                if (this.Description == null || this.Description == string.Empty)
                {
                    query.AppendLine("Uploaded from builderator");
                }
                else
                {
                    query.AppendLine(this.Description);
                }

                foreach (string label in this.Labels)
                {
                    query.AppendLine("--BASSHAMMAHSSAB");

                    query.AppendLine("Content-Disposition: form-data; name=\"label\"");

                    query.AppendLine();

                    query.AppendLine(label);
                }

                query.AppendLine("--BASSHAMMAHSSAB");

                query.AppendLine(string.Format("Content-Disposition: form-data; name=\"filename\"; filename=\"{0}\"", fileName));

                query.AppendLine("Content-Type: application/octet-stream");

                query.AppendLine();

                this.WriteStringToStream(requestStream, query.ToString());

                using (FileStream fileStream = System.IO.File.OpenRead(fileName))
                {
                    int bufferSize = 4096, count = 0, percent = 0, lastPercent = 0;
                    double fileSize = (double)fileStream.Length, currentFilePosition = 0.0;
                    byte[] buffer = new byte[bufferSize];

                    while ((count = fileStream.Read(buffer, 0, bufferSize)) > 0)
                    {
                        currentFilePosition += count;

                        percent = (int)(Math.Round(currentFilePosition/fileSize, 2) * 100);

                        if (percent > lastPercent)
                        {
                            this.DebugMessage(string.Format("Percent uploaded {0}", percent));

                            lastPercent = percent;
                        }

                        requestStream.Write(buffer, 0, bufferSize);
                    }
                }

                query = new StringBuilder();

                query.AppendLine();

                query.AppendLine("--BASSHAMMAHSSAB--");

                this.WriteStringToStream(requestStream, query.ToString());
            }

            try
            {
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    this.DebugMessage(response.Headers.ToString());
                }
            }
            catch (WebException e)
            {
                if (e.Response != null)
                {
                    HttpWebResponse response = (HttpWebResponse)e.Response;

                    this.DebugMessage(response.StatusDescription);

                    string responseBody;

                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        responseBody = reader.ReadToEnd();

                        this.DebugMessage(responseBody);
                    }

                    throw new Exception(responseBody, e);
                }
            }
        }

        private void WriteStringToStream(Stream stream, string str)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(str);

            stream.Write(buffer, 0, buffer.Length);
        }

        private string GetAuthToken()
        {
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Format("{0}:{1}", this.Username, this.Password)));
        }
    }
}